package org.example.springaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
public class MyAspect {

    @Pointcut("execution(* org.example.springaop.*..test(..))")
    public void test() {

    }

    @Pointcut("execution(* org.example.springaop.*..toString())")
    public void string() {

    }

    @Before("string()")
    public void before(JoinPoint jp) {
        Logger log = getAspectLogger(jp);
        log.debug("before");
    }

    @AfterReturning("string()")
    public void after(JoinPoint jp) {
        Logger log = getAspectLogger(jp);
        log.debug("after returning");
    }

    @AfterThrowing("test()")
    public void error(JoinPoint jp) {
        Logger log = getAspectLogger(jp);
        log.debug("fail");
    }

    @Around("test()")
    public String doTest(ProceedingJoinPoint pjp) throws Throwable {
        Logger log = getAspectLogger(pjp);
        log.debug("begin");
        Object[] args = pjp.getArgs();
        log.debug("args: ({}) {}", args.length, arrayToString(args));
        Object retVal;
        try {
            retVal = pjp.proceed();
        } catch (Exception e) {
            log.error("ERROR: {}", e.getMessage());
            return null;
        }
        log.debug("return \"{}\"", retVal);
        log.debug("end");
        return retVal + " (modified)";
    }

    private static String arrayToString(Object[] args) {
        StringBuilder builder = new StringBuilder();
        for (Object arg : args) {
            if (arg instanceof String s) {
                builder.append("\"");
                builder.append(s.replace("\"", "\\\""));
                builder.append("\"");
            } else {
                builder.append(arg);
            }
        }
        return builder.toString();
    }

    private static final Logger _log = LoggerFactory.getLogger(MyAspect.class);

    private static AspectLogger getAspectLogger(JoinPoint pjp) {
        return new AspectLogger(_log, pjp);
    }
}
